package com.easygoods.service;

import com.bestvike.linq.Linq;
import com.bestvike.stone.core.lang.Convert;
import com.bestvike.stone.core.lang.RandomUtils;
import com.bestvike.stone.core.lang.StringUtils;
import com.bestvike.stone.core.time.DateUtils;
import com.bestvike.stone.spring.exception.BusinessException;
import com.bestvike.stone.spring.exception.ErrorCode;
import com.bestvike.stone.spring.service.BaseService;
import com.easygoods.dao.DrawCashInfoDao;
import com.easygoods.dao.MemberCashRecordDao;
import com.easygoods.dao.MemberEarnRecordDao;
import com.easygoods.dao.MemberInfoDao;
import com.easygoods.dao.OrderInfoDao;
import com.easygoods.entity.DrawCashInfo;
import com.easygoods.entity.MemberCashRecord;
import com.easygoods.entity.MemberEarnRecord;
import com.easygoods.entity.MemberInfo;
import com.easygoods.entity.OrderGoods;
import com.easygoods.entity.OrderInfo;
import com.easygoods.enums.YNEnum;
import com.easygoods.shiro.auth.UserAuth;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.List;

/**
 * Created by 许崇雷 on 2019-01-22.
 */
@Service
public class CashService extends BaseService {
    @Autowired
    private OrderInfoDao orderInfoDao;
    @Autowired
    private MemberService memberService;
    @Autowired
    private ConfigService configService;
    @Autowired
    private GoodsService goodsService;
    @Autowired
    private MemberInfoDao memberInfoDao;
    @Autowired
    private MemberCashRecordDao memberCashRecordDao;
    @Autowired
    private DrawCashInfoDao drawCashInfoDao;
    @Autowired
    private MemberEarnRecordDao memberEarnRecordDao;
    @Autowired
    private CouponService couponService;
    @Autowired
    private PointService pointService;
    @Autowired
    private PayPwdService payPwdService;

    //生成订单号
    private static String genOrderId() {
        return DateUtils.nowString("yyyyMMddHHmmssSSS") + StringUtils.leftPad(String.valueOf(RandomUtils.nextInt(1, 99999)), 5, '0');
    }

    //给指定用户增加现金--首次购买,邀请
    @SuppressWarnings("Duplicates")
    private String increaseFirstInviteCash(String memberId, String acpMemberId, String acpMemberMobile, BigDecimal cash, String gainOrderId, String reason) {
        Assert.hasText(memberId, "memberId can not be empty");
        Assert.notNull(cash, "cash can not be null");
        Assert.isTrue(cash.compareTo(BigDecimal.ZERO) > 0, "cash must greater than 0");
        Assert.hasText(reason, "reason can not be empty");

        //增加金额
        final int count = this.memberInfoDao.increaseFirstInviteCash(memberId, cash);
        if (count != 1)
            throw new BusinessException(ErrorCode.ERROR_SERVER, "无效的用户");

        //记录日志
        MemberCashRecord memberCashRecord = new MemberCashRecord();
        memberCashRecord.setId(RandomUtils.randomUUID());
        memberCashRecord.setMemberId(memberId);
        memberCashRecord.setAmount(cash);
        memberCashRecord.setReason(reason);
        memberCashRecord.setAcpMemberId(acpMemberId);
        memberCashRecord.setAcpMemberMobile(acpMemberMobile);
        memberCashRecord.setRefGainOrderId(gainOrderId);
        memberCashRecord.setRefGainTeamId(null);
        memberCashRecord.setRefPayOrderId(null);//支付
        memberCashRecord.setRefDrawCashId(null);//提现
        memberCashRecord.beforeInsert();
        int num = this.memberCashRecordDao.insert(memberCashRecord);
        if (num != 1) {
            throw new BusinessException(ErrorCode.ERROR_SERVER, "新增用户余额记录失败");
        }
        return memberCashRecord.getId();
    }

    //给指定用户增加现金,多次购买--邀请
    @SuppressWarnings("Duplicates")
    private String increaseManyInviteCash(String memberId, String acpMemberId, String acpMemberMobile, BigDecimal cash, String gainOrderId, String reason) {
        Assert.hasText(memberId, "memberId can not be empty");
        Assert.notNull(cash, "cash can not be null");
        Assert.isTrue(cash.compareTo(BigDecimal.ZERO) > 0, "cash must greater than 0");
        Assert.hasText(reason, "reason can not be empty");

        //增加金额
        final int count = this.memberInfoDao.increaseManyInviteCash(memberId, cash);
        if (count != 1)
            throw new BusinessException(ErrorCode.ERROR_SERVER, "无效的用户");

        //记录日志
        MemberCashRecord memberCashRecord = new MemberCashRecord();
        memberCashRecord.setId(RandomUtils.randomUUID());
        memberCashRecord.setMemberId(memberId);
        memberCashRecord.setAmount(cash);
        memberCashRecord.setReason(reason);
        memberCashRecord.setAcpMemberId(acpMemberId);
        memberCashRecord.setAcpMemberMobile(acpMemberMobile);
        memberCashRecord.setRefGainOrderId(gainOrderId);
        memberCashRecord.setRefGainTeamId(null);
        memberCashRecord.setRefPayOrderId(null);//支付
        memberCashRecord.setRefDrawCashId(null);//提现
        memberCashRecord.beforeInsert();
        int num = this.memberCashRecordDao.insert(memberCashRecord);
        if (num != 1) {
            throw new BusinessException(ErrorCode.ERROR_SERVER, "新增用户余额记录失败");
        }
        return memberCashRecord.getId();
    }

    //团队结算增加现金,团长或团员
    @Transactional
    public void increaseTeamCash(String memberId, BigDecimal cash, String gainTeamId, String reason) {
        Assert.hasText(memberId, "memberId can not be empty");
        Assert.notNull(cash, "cash can not be null");
        Assert.isTrue(cash.compareTo(BigDecimal.ZERO) > 0, "cash must greater than 0");
        Assert.hasText(reason, "reason can not be empty");

        //增加金额
        final int count = this.memberInfoDao.increaseTeamCash(memberId, cash);
        if (count != 1)
            throw new BusinessException(ErrorCode.ERROR_SERVER, "无效的用户");
        //记录日志
        MemberCashRecord memberCashRecord = new MemberCashRecord();
        memberCashRecord.setId(RandomUtils.randomUUID());
        memberCashRecord.setMemberId(memberId);
        memberCashRecord.setAmount(cash);
        memberCashRecord.setReason(reason);
        memberCashRecord.setAcpMemberId(null);
        memberCashRecord.setAcpMemberMobile(null);
        memberCashRecord.setRefGainOrderId(null);
        memberCashRecord.setRefGainTeamId(gainTeamId);
        memberCashRecord.setRefPayOrderId(null);//支付
        memberCashRecord.setRefDrawCashId(null);//提现
        memberCashRecord.beforeInsert();
        int num = this.memberCashRecordDao.insert(memberCashRecord);
        if (num != 1) {
            throw new BusinessException(ErrorCode.ERROR_SERVER, "新增用户余额记录失败");
        }
    }

    //完成订单后,给邀请人返现
    @Transactional
    public String increaseRefMemberPointCashCoupon(OrderInfo orderInfo, List<OrderGoods> orderGoodsList, boolean firstBuy, int pointValue) {
        final MemberInfo refMemberInfo = this.memberService.getMemberInfo(orderInfo.getRefMemberId());//获取邀请人信息
        int refMemberGrad = refMemberInfo.getGrade();//用户等级
        final BigDecimal returnRatio = this.configService.getReturnRatio();//获取激励比例

        //给邀请人积分,邀请人等级>=1,则邀请人获得 30% 积分
        final int refPointValue = BigDecimal.valueOf(pointValue).multiply(this.configService.getAutoPointRate()).intValue();
        if (refMemberGrad >= this.configService.getAutoPointGrade() && refPointValue > 0) {
            String reason = String.format("下级 %s 购买商品", MemberService.messMobile(orderInfo.getMemberMobile()));
            this.pointService.increasePoint(refMemberInfo.getId(), refPointValue, reason, null);
        }

        //计算给邀请人的返现
        final BigDecimal cash = Linq.asEnumerable(orderGoodsList)
                .sumDecimal(orderGoods -> {
                    BigDecimal refMemberPrice = this.goodsService.getMemberPrice(refMemberGrad, orderGoods.getMinPrice(), orderGoods.getOriginPrice());
                    return this.computeCash(firstBuy, orderGoods, refMemberPrice, returnRatio);
                });

        //首次购买给邀请人返现，并给邀请人赠送一张代金券
        if (firstBuy) {
            final String reason = String.format("邀请 %s 注册首购,赠送代金券", orderInfo.getMemberMobile());
            this.couponService.addCoupon(refMemberInfo.getId(), orderInfo.getMemberId(), orderInfo.getMemberMobile(), this.configService.getFirstBuyCouponAmount(), this.configService.getFirstBuyCouponValidDays(), reason, null);
            return this.increaseFirstInviteCash(orderInfo.getRefMemberId(), orderInfo.getMemberId(), orderInfo.getMemberMobile(), cash, orderInfo.getId(), "首次完成订单");
        }

        //非首次购买给邀请人返现
        return this.increaseManyInviteCash(orderInfo.getRefMemberId(), orderInfo.getMemberId(), orderInfo.getMemberMobile(), cash, orderInfo.getId(), "多次完成订单");
    }

    //该用户邀请的用户已购买、未结算商品的收益计算
    public BigDecimal computeInviteUnsettledGoods() {
        MemberInfo memberInfo = this.memberService.getMemberInfo();
        int memberGrad = memberInfo.getGrade();//用户等级
        BigDecimal returnRatio = this.configService.getReturnRatio();//获取激励比例
        List<OrderGoods> orderGoodsList = this.orderInfoDao.getInvitedUnsettledOrderGoods(memberInfo.getId());
        //计算给自己的待返现的金额
        return Linq.asEnumerable(orderGoodsList)
                .sumDecimal(orderGoods -> {
                    BigDecimal memberPrice = this.goodsService.getMemberPrice(memberGrad, orderGoods.getMinPrice(), orderGoods.getOriginPrice());
                    return this.computeCash(false, orderGoods, memberPrice, returnRatio);
                });
    }

    //完成订单后，计算该笔订单的可赚金额
    public String increaseEarnCash(OrderInfo orderInfo, List<OrderGoods> orderGoodsList, String reason) {
        MemberInfo memberInfo = this.memberService.getMemberInfo(orderInfo.getMemberId());
        int grade = memberInfo.getGrade();
        BigDecimal orderEarnCash = BigDecimal.ZERO;
        for (OrderGoods orderGoods : orderGoodsList) {
            BigDecimal memberPrice = this.goodsService.getMemberPrice(grade, orderGoods.getMinPrice(), orderGoods.getOriginPrice());
            BigDecimal goodsEarnCash = orderGoods.getOriginPrice().subtract(memberPrice).multiply(new BigDecimal(orderGoods.getNum()));
            orderEarnCash = orderEarnCash.add(goodsEarnCash);
        }
        int count = this.memberInfoDao.increaseEarnCash(memberInfo.getId(), orderEarnCash);
        if (count != 1) {
            throw new BusinessException(ErrorCode.ERROR_SERVER, "用户增加商品可赚金额失败");
        }
        MemberEarnRecord memberEarnRecord = new MemberEarnRecord();
        memberEarnRecord.setId(RandomUtils.randomUUID());
        memberEarnRecord.setMemberId(memberInfo.getId());
        memberEarnRecord.setAmount(orderEarnCash);
        memberEarnRecord.setReason(reason);
        memberEarnRecord.setRefOrderId(orderInfo.getId());
        memberEarnRecord.beforeInsert();
        this.memberEarnRecordDao.insert(memberEarnRecord);
        return memberEarnRecord.getId();
    }

    //申请售后,扣减返现
    @Transactional
    public void deductCash(String cashRecordId, String reason) {
        Assert.notNull(cashRecordId, "cashRecordId can not be null");
        MemberCashRecord memberCashRecord = this.memberCashRecordDao.selectByPrimaryKey(cashRecordId);
        if (memberCashRecord == null) {
            this.log.error("扣减邀请人返现失败 cashRecordId:" + cashRecordId);
            throw new BusinessException(ErrorCode.ERROR_SERVER, "操作失败");
        }
        final String memberId = memberCashRecord.getMemberId();
        final BigDecimal amount = memberCashRecord.getAmount();
        Assert.notNull(memberId, "memberId 不能为 null");
        Assert.notNull(amount, "原始金额为 null,无法扣减");
        Assert.isTrue(amount.compareTo(BigDecimal.ZERO) > 0, "原始金额必须大于 0");
        //TODO 扣减金额,可能出现负数
        this.memberInfoDao.increaseFirstInviteCash(memberId, amount.negate());
        //记录日志
        MemberCashRecord memberCashRecordInsert = new MemberCashRecord();
        memberCashRecordInsert.setId(RandomUtils.randomUUID());
        memberCashRecordInsert.setMemberId(memberId);
        memberCashRecordInsert.setAmount(amount.negate());
        memberCashRecordInsert.setReason(reason);
        memberCashRecordInsert.setAcpMemberId(memberCashRecord.getAcpMemberId());
        memberCashRecordInsert.setAcpMemberMobile(memberCashRecord.getAcpMemberMobile());
        memberCashRecordInsert.setRefGainOrderId(memberCashRecord.getRefGainOrderId());
        memberCashRecordInsert.setRefPayOrderId(memberCashRecord.getRefPayOrderId());//支付
        memberCashRecordInsert.setRefDrawCashId(memberCashRecord.getRefDrawCashId());//提现
        memberCashRecordInsert.beforeInsert();
        int num = this.memberCashRecordDao.insert(memberCashRecordInsert);
        if (num != 1) {
            this.log.error("新增用户现金记录失败");
            throw new BusinessException(ErrorCode.ERROR_SERVER, "用户金额操作失败");
        }
    }

    //创建提现信息
    @Transactional
    public DrawCashInfo drawCash(final String payPwd, final BigDecimal cash, String reason) {
        Assert.hasText(payPwd, "payPwd can not be empty");
        Assert.notNull(cash, "cash can not be null");
        Assert.isTrue(cash.compareTo(BigDecimal.ZERO) > 0, "金额必须大于 0");

        //验证短信验证码
        this.payPwdService.checkPayPwd(payPwd);

        int cents = cash.multiply(new BigDecimal("100")).intValue();
        Assert.isTrue(cents > 0, "金额转换为分必须大于 0");

        final UserAuth userAuth = this.getAuth();
        //扣除金额
        final int count = this.memberInfoDao.decreaseCash(userAuth.getMemberId(), cash);
        if (count != 1)
            throw new BusinessException(ErrorCode.ERROR_SERVER, "无效的用户");
        //判断余额
        final MemberInfo memberInfo = this.memberService.getMemberInfo();
        if (memberInfo.getRemainCash().compareTo(BigDecimal.ZERO) < 0)
            throw new BusinessException(ErrorCode.ERROR_SERVER, "您的余额不足, 无法提现");
        //保存提现单据
        if (userAuth.getChannel() == null)
            throw new BusinessException(ErrorCode.ERROR_SERVER, "暂不支持的渠道");
        switch (userAuth.getChannel()) {
            case WeChat:
                break;
            case AliPay:
                throw new BusinessException(ErrorCode.ERROR_SERVER, "暂不支持支付宝");
            default:
                throw new BusinessException(ErrorCode.ERROR_SERVER, "敬请期待");
        }
        final DrawCashInfo drawCashInfo = new DrawCashInfo();
        drawCashInfo.setId(genOrderId());
        drawCashInfo.setMemberId(userAuth.getMemberId());
        drawCashInfo.setMobile(userAuth.getMobile());
        drawCashInfo.setTransferPayChannel(userAuth.getChannel().getPayChannel().name());
        drawCashInfo.setTransferOpenId(userAuth.getOpenId());
        drawCashInfo.setTransferAmount(cash);
        drawCashInfo.setTransferTradeNo(null);
        drawCashInfo.setTransferTradeTime(null);
        drawCashInfo.setTransferFinished(YNEnum.N.name());
        drawCashInfo.beforeInsert();
        final int insert = this.drawCashInfoDao.insert(drawCashInfo);
        if (insert != 1)
            throw new BusinessException(ErrorCode.ERROR_SERVER, "提现失败");
        //记录现金日志
        MemberCashRecord memberCashRecord = new MemberCashRecord();
        memberCashRecord.setId(RandomUtils.randomUUID());
        memberCashRecord.setMemberId(userAuth.getMemberId());
        memberCashRecord.setAmount(cash);
        memberCashRecord.setReason(reason);
        memberCashRecord.setAcpMemberId(null);
        memberCashRecord.setAcpMemberMobile(null);
        memberCashRecord.setRefGainOrderId(null);
        memberCashRecord.setRefPayOrderId(null);//支付
        memberCashRecord.setRefDrawCashId(drawCashInfo.getId());//提现
        memberCashRecord.beforeInsert();
        int num = this.memberCashRecordDao.insert(memberCashRecord);
        if (num != 1) {
            throw new BusinessException(ErrorCode.ERROR_SERVER, "新增用户余额记录失败");
        }
        return drawCashInfo;
    }

    //计算返现,根据是否第一次购买
    private BigDecimal computeCash(boolean firstBuy, OrderGoods orderGoods, BigDecimal refMemberPrice, BigDecimal returnRatio) {
        BigDecimal refIncentive = firstBuy
                ? orderGoods.getOriginPrice().subtract(refMemberPrice)
                : orderGoods.getOriginPrice().subtract(orderGoods.getMemberPrice()).multiply(returnRatio);
        return Convert.toDecimal(orderGoods.getNum()).multiply(refIncentive);
    }
}
